package imse.team0.scalability.logging;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.LoggingEvent;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.Properties;

public class LoggingServer {
    static Logger cat = Logger.getLogger(LoggingServer.class);
    static String ip;
    static int port;

    public static void main(String[] argv) throws IOException {
        if (argv.length == 2)
            init(argv[0], argv[1]);
        else {
            usage("Wrong number of arguments.");
        }
        try {
            cat.info("Listening on: " + ip + ":" + port);
            ServerSocket serverSocket = new ServerSocket();
            serverSocket.setReuseAddress(true);
            serverSocket.bind(new InetSocketAddress(ip, port));
            while (true) {
                cat.info("Waiting to accept a new client.");
                Socket socket = serverSocket.accept();
                cat.info("Connected to client at " + socket.getInetAddress());
                cat.info("Starting new socket node.");
                Thread th = new Thread(new SocketWriter(socket, LogManager.getLoggerRepository()), "LoggingServer for host -" + socket.getInetAddress());
                th.setDaemon(true);
                th.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void usage(String msg) {
        System.err.println(msg);
        System.err.println("Usage: java " + LoggingServer.class.getName() + " ip port");

        System.exit(1);
    }

    static void init(String ipStr, String portStr) throws IOException {
        try {
            ip = ipStr;
            port = Integer.parseInt(portStr);
        } catch (NumberFormatException e) {
            e.printStackTrace();
            usage("Could not interpret port number [" + portStr + "].");
        }
        URL url = LoggingServer.class.getResource("/log4j.properties");
        Properties props = new Properties();
        props.load(url.openStream());
        //File logFile = new File(props.getProperty("log4j.appender.local.file"));
        // clean previous logs on startup.
        //FileWriter out = new FileWriter(logFile, false);
        //out.write(' ');
        //out.close();
        PropertyConfigurator.configure(props);
    }
}

class SocketWriter implements Runnable {
    private Socket conn;
    private LoggerRepository logRepo;
    private ObjectInputStream ois;

    SocketWriter(Socket conn, LoggerRepository logRepo) {
        this.conn = conn;
        this.logRepo = logRepo;

        try {
            ois = new ObjectInputStream(conn.getInputStream());
        } catch (IOException e) {
            LoggingServer.cat.error("Error while getting ObjectInputStream from socket connection", e);
        }
    }

    public void run() {
        if (ois == null) {
            try {
                conn.close();
            } catch (IOException e) {
                LoggingServer.cat.warn("Error in closing the socket connection for :"+conn.getInetAddress(), e);
            }
            return;
        }

        try {
            while (true) {

                LoggingEvent event = (LoggingEvent) ois.readObject();
                //System.out.println("Received: ["+event.getLoggerName()+"] "+event.getMessage());
                // add ip information to the logged message
                //String msg = "{" + conn.getInetAddress() + "}" + event.getRenderedMessage();
                Logger log = logRepo.getLogger(event.getLoggerName());
                //Enumeration appenders = log.getAllAppenders();
                //while (appenders.hasMoreElements()){
                //    System.out.println("Appender for log=" + log.getName() + " : " + ((Appender) appenders.nextElement()).getName());
                //}
                //System.out.println("Received message: "+event.getMessage());
                log.log(event.getLevel(), event.getMessage());

            }
        } catch (IOException e) {
            LoggingServer.cat.error("Error while unmarshalling LoggingEvent object", e);
        } catch (ClassNotFoundException e) {
            LoggingServer.cat.error("Error while unmarshalling LoggingEvent object", e);
        }
        finally{
            try {
                ois.close();
                conn.close();
            } catch (IOException e) {
                LoggingServer.cat.warn("Error in closing the socket connection for :"+conn.getInetAddress(), e);
            }
        }
    }
}
